package domain

import (
	"fmt"
	"io"
	"net"
	"runtime"
	"sync"
	"time"
)

type Server struct {
	Ip        string
	Port      int
	OnlineMap map[string]*Client
	mapLock   sync.RWMutex
	msgChan   chan string
}

func (server *Server) Start() {
	host := fmt.Sprintf("%s:%d", server.Ip, server.Port)
	listen, err := net.Listen("tcp", host)
	if err != nil {
		fmt.Println("net.Listen err:", err)
		return
	}

	defer func(listen net.Listener) {
		err := listen.Close()
		if err != nil {
			fmt.Println("net.Close err:", err)
		}
	}(listen)

	go server.ListenMessage()

	for {
		conn, err := listen.Accept()
		if err != nil {
			fmt.Println("net.accept err:", err)
			continue
		}
		go server.Handler(conn)
	}
}

func (server *Server) Handler(conn net.Conn) {
	fmt.Println("net.Conn succeed... addr:", conn.RemoteAddr())
	cli := NewClientForNc(conn, server)
	pulse := make(chan bool)

	cli.Online()

	go converse(cli, pulse)

	refresh(cli, pulse)
}

func refresh(cli *Client, pulse chan bool) {
	for {
		select {
		case <-pulse:
			fmt.Println("net.Conn can breath... addr:", cli.Conn.RemoteAddr())
		case <-time.After(time.Second * 10):
			cli.whisper("You is offline. due to time out")
			close(cli.MsgChan)
			_ = cli.Conn.Close()
			fmt.Println("net.Conn closed... addr:", cli.Conn.RemoteAddr())
			runtime.Goexit()
		}
	}
}

func converse(cli *Client, isLive chan bool) {
	conn := cli.Conn
	buf := make([]byte, 4096)
	for {
		read, err := conn.Read(buf)
		if read == 0 {
			cli.Offline()
			return
		}
		if err != nil && err != io.EOF {
			fmt.Println("Conn read err:", err)
			return
		}
		msg := string(buf[:read-1])
		cli.Handle(msg)
		isLive <- true
	}
}

func (server *Server) ListenMessage() {
	for {
		msg := <-server.msgChan
		server.mapLock.Lock()
		for _, cli := range server.OnlineMap {
			cli.MsgChan <- msg
		}
		server.mapLock.Unlock()
	}
}

func (server *Server) BroadCast(user *Client, message string) {
	sendMsg := user.toString(message)
	server.msgChan <- sendMsg
}

func NewServer(ip string, port int) *Server {
	server := &Server{
		Ip:        ip,
		Port:      port,
		OnlineMap: make(map[string]*Client),
		msgChan:   make(chan string),
	}
	return server
}

func ServerStart(ip string, port int) {
	server := NewServer(ip, port)
	server.Start()
}
